import datetime
import re
from django_filters.conf import settings
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.validators import UniqueValidator
from rest_framework_jwt.settings import api_settings

from rider.serializers import RiderSerializer
from station.serializers import StationSerializer
from users.models import Users, VerifyCode

# from django.contrib.auth import get_user_model
# User = get_user_model()


# 手机号码正则表达式
REGEX_MOBILE = "^1[358]\d{9}$|^147\d{8}$|^176\d{8}$"

# 根据手机号生成简易用户名
def user_name(value):
    if value is None:
        value = ''
    name = 'bwd'+value
    return name

class SmsSerializer(serializers.Serializer):
    """
    注册提交手机序列化类
    """
    phone = serializers.CharField(max_length=11)

    def validate_phone(self, phone):
        """
        自定义验证手机号码
        :return:
        """
        # 是否已经注册
        if Users.objects.filter(phone=phone).count():
            raise serializers.ValidationError("手机号已注册")

        # 是否合法
        if not re.match(REGEX_MOBILE, phone):
            raise serializers.ValidationError("手机号码非法")

        # 验证码发送频率
        # 60s内只能发送一次
        one_mintes_ago = datetime.datetime.now() - datetime.timedelta(hours=0, minutes=1, seconds=0)
        if VerifyCode.objects.filter(add_time__gt=one_mintes_ago, phone=phone).count():
            raise serializers.ValidationError("距离上一次发送未超过60s")

        return phone

class SmsLoginSerializer(serializers.Serializer):
    """
    登录提交手机序列化类
    """
    phone = serializers.CharField(max_length=11)

    def validate_phone(self, phone):
        """
        自定义验证手机号码
        :return:
        """
        # 是否已经注册
        if not Users.objects.filter(phone=phone).count():
            raise serializers.ValidationError("用户还未注册")

        # 是否合法
        if not re.match(REGEX_MOBILE, phone):
            raise serializers.ValidationError("手机号码非法")

        # 验证码发送频率
        # 60s内只能发送一次
        one_mintes_ago = datetime.datetime.now() - datetime.timedelta(hours=0, minutes=1, seconds=0)
        if VerifyCode.objects.filter(add_time__gt=one_mintes_ago, phone=phone).count():
            raise serializers.ValidationError("距离上一次发送未超过60s")

        return phone

class UserRegSerializer(serializers.ModelSerializer):
    """
    用户注册序列化类
    """
    # 增加只读token字段
    token = serializers.CharField(label='登录状态token', read_only=True)
    # UserProfile中没有code字段， 这里需要对应自定义一个code字段 否则会失败
    code = serializers.CharField(
        max_length=4, min_length=4, required=True, write_only=True,
        error_messages={
             "blank": "请输入验证码",
             "required": "请输入验证码",
             "max_length": "验证码格式错误",
             "min_length": "验证码格式错误"
         }, help_text="验证码"
    )
    # 验证用户名是否存在
    phone = serializers.CharField(label='手机号', help_text='手机号', required=True, allow_blank=False,
                                  validators=[UniqueValidator(queryset=Users.objects.all(), message="该手机号已经注册")],
                                  error_messages={
                                      "blank": "请输入手机号",
                                      "required": "请输入手机号",
                                  })

    password = serializers.CharField(
        style={'input_type': 'password'}, help_text="密码", label="密码", write_only=True,
        min_length=6, max_length=16, required=True, error_messages={
            "blank": "请输入密码",
            "required": "请输入密码",
            "max_length": "密码太长应小于16位",
            "min_length": "密码太短应大于6位"
        }
    )

    re_password = serializers.CharField(
        style={'input_type': 'password'}, help_text="重复密码", label="重复密码", write_only=True,
        min_length=6, max_length=16, required=True, error_messages={
            "blank": "请再次输入密码",
            "required": "请再次输入密码",
            "max_length": "密码太长应小于16位",
            "min_length": "密码太短应大于6位"
        }
    )

    username = serializers.CharField(label='用户名', read_only=True)

    role_type = serializers.CharField(label='用户类型', read_only=True)

    # 密码加密保存
    def create(self, validated_data):
        """
        注册用户
        """
        # 移除数据库模型类中不存在的属性
        # del validated_data['re_password']
        validated_data['username'] = user_name(validated_data['phone'])
        user = super().create(validated_data)

        # 调用django的认证系统加密密码
        user.set_password(validated_data['password'])
        user.save()

        # 补充生成记录登录状态的token
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        user.token = token

        return user

    # 验证code
    def validate_code(self, code):
        # 用户注册，已post方式提交注册信息，post的数据都保存在initial_data里面
        # username就是用户注册的手机号，验证码按添加时间倒序排序，为了后面验证过期，错误等
        verify_records = VerifyCode.objects.filter(phone=self.initial_data['phone']).order_by("-add_time")

        if verify_records:
            # 最近的一个验证码
            last_record = verify_records[0]
            # 有效为5分钟
            five_minutes_ago = datetime.datetime.now() - datetime.timedelta(hours=0, minutes=5, seconds=0)
            if five_minutes_ago > last_record.add_time:
                raise serializers.ValidationError("验证码过期")

            if last_record.code != code:
                # 说明输入的验证码是错误的 和数据中匹配不上
                raise serializers.ValidationError("验证码错误")
        else:
            raise serializers.ValidationError("请确认账号")

    def validate_phone(self, phone):
        # 验证前端传过来的phone
        if not re.match(REGEX_MOBILE, phone):
            raise serializers.ValidationError("手机号码非法")
        return phone

    # 所有字段验证完后的方法 attrs是字段验证合法后返回的dict
    def validate(self, attrs):
        # 验证两次密码是否一致
        pwd = attrs.get('password')
        re_pwd = attrs.get('re_password')
        if pwd == re_pwd:
            del attrs['re_password']  # 可以在全局钩子移除模型中没有的属性
            del attrs["code"]   # code是自己添加得，数据库中并没有这个字段，验证完就删除掉
            return attrs
        else:
            raise ValidationError('两次输入密码不一样')

    def is_valid(self, raise_exception=False):
        try:
            super().is_valid(raise_exception=True)
        except Exception as e:
            assert isinstance(e, ValidationError)
            # raise ValidationError({"err_msg": e.detail})
            for k, v in e.detail.items():
                raise ValidationError({"err_msg": v[0]})

    class Meta:
        model = Users
        fields = ('id', 'username', 'phone', 'code', 'password', 're_password', 'token', 'role_type')

class UserDetailSerializer(serializers.ModelSerializer):
    """
    用户详情序列化类
    """
    role_type = serializers.CharField(label='用户类型', read_only=True)
    phone = serializers.CharField(label='手机号', read_only=True)
    rider = RiderSerializer(many=True, read_only=True)

    class Meta:
        model = Users
        fields = ('id', 'username', 'phone', 'role_type', 'email', 'real_name', 'birthday', 'head_img', 'rider')

class SMSLoginSerializer(serializers.ModelSerializer):
    # 增加只读token字段
    token = serializers.CharField(label='登录状态token', read_only=True)
    # UserProfile中没有code字段， 这里需要对应自定义一个code字段 否则会失败
    code = serializers.CharField(
        max_length=4, min_length=4, required=True, write_only=True,
        error_messages={
            "blank": "请输入验证码",
            "required": "请输入验证码",
            "max_length": "验证码格式错误",
            "min_length": "验证码格式错误"
        }, help_text="验证码"
    )
    # 验证用户名是否存在
    phone = serializers.CharField(label='手机号', help_text='手机号', required=True, allow_blank=False,
                                  validators=[UniqueValidator(queryset=Users.objects.all(), message="该手机号已经注册")],
                                  error_messages={
                                      "blank": "请输入手机号",
                                      "required": "请输入手机号",
                                  })

    username = serializers.CharField(label='用户名', read_only=True)

    role_type = serializers.CharField(label='用户类型', read_only=True)

    # 密码加密保存
    def create(self, validated_data):
        """
        注册用户
        """
        # 移除数据库模型类中不存在的属性
        # del validated_data['re_password']
        validated_data['username'] = user_name(validated_data['phone'])
        user = super().create(validated_data)

        # 补充生成记录登录状态的token
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        user.token = token

        return user

    # 验证code
    def validate_code(self, code):
        # 用户注册，已post方式提交注册信息，post的数据都保存在initial_data里面
        # username就是用户注册的手机号，验证码按添加时间倒序排序，为了后面验证过期，错误等
        verify_records = VerifyCode.objects.filter(phone=self.initial_data['phone']).order_by("-add_time")

        if verify_records:
            # 最近的一个验证码
            last_record = verify_records[0]
            # 有效为5分钟
            five_minutes_ago = datetime.datetime.now() - datetime.timedelta(hours=0, minutes=5, seconds=0)
            if five_minutes_ago > last_record.add_time:
                raise serializers.ValidationError("验证码过期")

            if last_record.code != code:
                # 说明输入的验证码是错误的 和数据中匹配不上
                raise serializers.ValidationError("验证码错误")
        else:
            raise serializers.ValidationError("请确认账号")

    def validate_phone(self, phone):
        # 验证前端传过来的phone
        if not re.match(REGEX_MOBILE, phone):
            raise serializers.ValidationError("手机号码非法")
        return phone

    def is_valid(self, raise_exception=False):
        try:
            super().is_valid(raise_exception=True)
        except Exception as e:
            assert isinstance(e, ValidationError)
            raise ValidationError({"err_msg": e.detail})
            # for k, v in e.detail.items():
            #     raise ValidationError({"err_msg": v[0]})

    class Meta:
        model = Users
        fields = ('id', 'username', 'phone', 'code', 'token', 'role_type')

class PasswordLoginSerializer(serializers.ModelSerializer):
    # 增加只读token字段
    # token = serializers.CharField(label='登录状态token', read_only=True)

    username = serializers.CharField(label='用户名')

    password = serializers.CharField(
        style={'input_type': 'password'}, help_text="密码", label="密码", write_only=True,
        required=True, error_messages={
            "blank": "请输入密码",
            "required": "请输入密码"
        }
    )

    role_type = serializers.CharField(label='用户类型', read_only=True)

    def get_user_by_account(self, account):
        """
        根据帐号获取user对象
        :param account: 账号，可以是用户名，也可以是手机号
        :return: Users对象 或者 None
        """
        user = None
        try:
            if re.match('^1[3-9]\d{9}$', account):
                # 帐号为手机号
                user = Users.objects.get(phone=account)
            elif re.match('^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$', account):
                # 账号为邮箱
                user = Users.objects.get(email=account)
            else:
                # 帐号为用户名
                user = Users.objects.get(username=account)
        except Users.DoesNotExist:
            user = None
        finally:
            return user

    def validate(self, attrs):
        # 验证两次密码是否一致
        password = attrs.get('password')
        username = attrs.get('username')
        user = self.get_user_by_account(username)
        if user is not None and user.check_password(password):
            # 补充生成记录登录状态的token
            jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
            jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
            payload = jwt_payload_handler(user)
            token = jwt_encode_handler(payload)
            user.token = token
            return {
                'token': token,
                'id': user.id,
                'username': user.username,
                'role_type': user.role_type,
            }
        else:
            raise ValidationError('用户名或密码错误!')

    def is_valid(self, raise_exception=False):
        try:
            super().is_valid(raise_exception=True)
        except Exception as e:
            assert isinstance(e, ValidationError)
            for k, v in e.detail.items():
                raise ValidationError({"err_msg": v[0]})

    class Meta:
        model = Users
        fields = ('id', 'username', 'password', 'role_type')